<?php
/**
 * Copyright (c) 2006-2008, Julien PORTALIER
 * http://featherphp.googlecode.com/
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 */

/**
 * Manages ActiveRecord's validation errors.
 */
class ActiveResource_Request
{
	protected $options = array(
		CURLOPT_RETURNTRANSFER => true,
		CURLOPT_CONNECTTIMEOUT => 10,
		CURLOPT_TIMEOUT        => 20,
		CURLOPT_HTTPHEADER     => array(),
	);
	protected $headers = array(
		'Accept-Charset' => Core::charset,
		'Expect'         => ''
	);
	protected $debugs = array();
	protected $auth;
	
	function __construct(array $headers=array())
	{
		$this->headers = array_merge($this->headers, $headers);
	}
	
	function set_authentication($protocol, $username, $password)
	{
		$this->auth = array(
			'protocol' => $protocol,
			'username' => $username,
			'password' => $password,
		);
	}
	
	/**
	 * Runs a request.
	 * 
	 * @return Array array($http_code, $response)
	 * @param $method String[optional]
	 * @param $url String
	 * @param $params Mixed[optional]
	 */
	function run($method='GET', $url, $params=null)
	{
		# authentication
		if (!empty($this->auth))
		{
			Load::_include('models'.DS.'authentications'.DS.Inflector::underscore($this->auth['protocol']));
			$func = "ActiveResource_Authentication_{$this->auth['protocol']}";
			$func($this->auth['username'], $this->auth['password'], $this->headers, $params, $method, $url);
		}

		$options = $this->options;
		
		# prepares headers
		foreach($this->headers as $k => $v) {
			$options[CURLOPT_HTTPHEADER][] = "$k: $v";
		}
		
		# prepares the request
		if ($method == 'GET') {
			$options[CURLOPT_HTTPGET] = true;
		}
		elseif ($method == 'POST' or $method == 'PUT')
		{
			if ($method == 'POST')
			{
				$options[CURLOPT_POST] = true;
				if (!empty($params)) {
					$options[CURLOPT_POSTFIELDS] = $params;
				}
			}
			if ($method == 'PUT')
			{
				$options[CURLOPT_PUT] = true;
				if (!empty($params))
				{
					$str_params = http_build_query($params);
					$put_params = tmpfile();
					fwrite($put_params, $str_params);
					fseek($put_params, 0);
					
					$options[CURLOPT_INFILE]     = $put_params;
					$options[CURLOPT_INFILESIZE] = strlen($str_params);
				}
			}
		}
		elseif ($method == 'DELETE') {
			$options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
		}
		
		# performs the request
		$req = curl_init($url);
		curl_setopt_array($req, &$options);
		$response = curl_exec($req);
		
		if (isset($put_params)) {
			fclose($put_params);
		}
		
#		echo '<pre>';
#		echo $response;
#		echo '</pre>';
#		var_dump(curl_getinfo($req));
		
		if (DEBUG) {
			$this->debug($method, $req);
		}
		
		# ok
		if ($response !== false)
		{
			$code = curl_getinfo($req, CURLINFO_HTTP_CODE);
			curl_close($req);
			return array($code, $response);
		}
		
		# an error occured
		$code = curl_getinfo($req, CURLINFO_HTTP_CODE);
		curl_close($req);
		throw new ActiveResource_Exception($code);
	}
	
	/**
	 * Records the request (when in DEBUG mode).
	 */
	protected function debug($method, $curl)
	{
		$infos = curl_getinfo($curl);
		$q = array(
			'url'    => $method.' '.$infos['url'],
			'status' => $infos['http_code'],
			'error'  => curl_errno($curl).' '.curl_error($curl),
			'time'   => $infos['total_time']
		);
		array_push($this->debugs, $q);
	}
	
	/**
	 * Displays list of requests at the end of the page (when in DEBUG mode).
	 */
	function __destruct()
	{
		# no debug?
		if (DEBUG < 2 or (defined('DB_NO_DEBUG') and DB_NO_DEBUG))
		{
			return;
		}
		
		if (isset($_SERVER['SERVER_ADDR']))
		{
			echo '<table class="DBDebug" summary="HTTP requests">' .
				'<tr>' .
				'<th>requests ( '.count($this->debugs).' )</th>' .
				'<th>status</th>' .
				'<th>error</th>' .
				'<th>time</th>' .
				'</tr>';
			
			foreach($this->debugs as $d)
			{
				printf("\n<tr>" .
					"<td>%s</td>" .
					"<td>%d</td>" .
					"<td>%s</td>" .
					"<td>%.02fms</td>" .
					"</tr>",
					$d['url'], $d['status'], $d['error'], ($d['time'] * 1000));
			}
			echo '</table>';
		}
		else
		{
			echo "DEBUG [requests]\n";
			
			foreach($this->debugs as $d)
			{
				echo $d['sql']."\n";
				echo "\terror: ".$d['error']."\n";
				echo "\taffected: ".$d['affected']."\n";
				echo "\ttime: ".$d['time']."\n";
			}
			echo "\n";
		}
	}
}

/**
 * Handles ActiveResource exceptions.
 */
class ActiveResource_Exception extends Exception
{
	function __construct($code)
	{
		$message = HTTP::get_status($code);
		parent::__construct($message, $code);
	}
}
?>